#include "PreCompile.hpp"
#include "FileParser.hpp"
#include "TokenizerInput.hpp"
#include "PreProcDOM.hpp"
#include "IncludeDirective.hpp"
#include "IfDefDirective.hpp"
#include "IfDirective.hpp"

namespace CPreProc
{
  typedef std::string ParseError;

  void FileParser::parse(Glib::RefPtr<TokenizerInput> tokenizer_doc)
  {
    this->tokenizer_doc = tokenizer_doc;

    output_file = Glib::RefPtr<PreProcFile>(new PreProcFile);

    cur_group = output_file;
    
    TokenizerInput::LineIterator line_iterator = tokenizer_doc->get_line_iterator();
    while(line_iterator.move_next())
    {
      Tokenizer tokenizer(line_iterator.get_line_begin(),line_iterator.get_line_end());
      parse_line(tokenizer);
    }
  }

  void FileParser::parse_line(Tokenizer &tokenizer)
  {
    tokenizer.skip_optional_whitespace();
    if(tokenizer.end_of_data())
      return;
   
    Token token = tokenizer.read_pp_token();
    if(token.get_token_id() == Punct_Sharp)
    {
      // it's a preprocessor directive
      tokenizer.skip_optional_whitespace();
      
      Token directive_token = tokenizer.read_pp_token();
      if(directive_token.get_token_type() != TokenType_Identifier)
      {
        throw ParseError("Preprocessor directive expected.");
      }

      if(directive_token == "if")
      {
        Glib::RefPtr<IfCondition> condition = IfCondition::parse(tokenizer);
        begin_if_section(condition);
      }
      else if(directive_token == "ifdef")
      {
        Glib::RefPtr<IfDefCondition> condition = IfDefCondition::parse(tokenizer,0);
        begin_if_section(condition);
      }
      else if(directive_token == "ifndef")
      {
        Glib::RefPtr<IfDefCondition> condition = IfDefCondition::parse(tokenizer,
          IfDefCondition::ParseFlag_IsIfNDef);
        begin_if_section(condition);
      }
      else if(directive_token == "elif")
      {
        Glib::RefPtr<IfCondition> condition = IfCondition::parse(tokenizer);
        begin_elif_block(condition);
      }
      else if(directive_token == "else")
      {
        begin_else_block();
      }
      else if(directive_token == "endif")
      {
        end_if_section();
      }
      else if(directive_token == "include")
      {
        Glib::RefPtr<IncludeDirective> include_directive = 
          IncludeDirective::parse(tokenizer,true);
        cur_group->group_parts.push_back(include_directive);
      }
      else if(directive_token == "define")
      {
        Glib::RefPtr<DefineDirective> define_directive =
          DefineDirective::parse(tokenizer,true);
        cur_group->group_parts.push_back(define_directive);
      }
      else if(directive_token == "undef")
      {
        Glib::RefPtr<UndefDirective> undef_directive = 
          UndefDirective::parse(tokenizer,true);
        cur_group->group_parts.push_back(undef_directive);
      }
      else if(directive_token == "line")
      {
        assert(!"not implemented yet");
      }
      else if(directive_token == "error")
      {
        Glib::RefPtr<ErrorDirective> error_directive =
          ErrorDirective::parse(tokenizer,true);
        cur_group->group_parts.push_back(error_directive);
      }
      else if(directive_token == "pragma")
      {
        parse_pragma(tokenizer);
      }
      else
      {
        assert(!"not implemented yet");
      }
    }
    else
    {
      // TODO: implement handling for content
    }
  }

  void FileParser::parse_pragma(Tokenizer &tokenizer)
  {
    assert(!"not implemented yet");
  }

  void FileParser::begin_if_section(Glib::RefPtr<IfGroupCondition> condition)
  {
    // begin a new if section

    IfSectionInfo info;
    info.if_section = Glib::RefPtr<IfSection>(new IfSection);
    info.has_else_block = false;

    cur_group->group_parts.push_back(info.if_section);

    Glib::RefPtr<IfGroup> if_group = Glib::RefPtr<IfGroup>(new IfGroup);
    if_group->condition = condition;
    info.if_section->if_groups.push_back(if_group);

    if_sections_stack.push(info);
    cur_group = if_group;
  }

  void FileParser::begin_elif_block(Glib::RefPtr<IfGroupCondition> condition)
  {
    if(!if_sections_stack.size())
      throw ParseError("#elif Directive doesn't correspond to a previous #if directive.");

    IfSectionInfo &cur_if_section = if_sections_stack.top();
    if(cur_if_section.has_else_block)
      throw ParseError("An #elif directive can't follow an #else directive.");

    Glib::RefPtr<IfGroup> elif_group = Glib::RefPtr<IfGroup>(new IfGroup);
    elif_group->condition = condition;
    cur_if_section.if_section->if_groups.push_back(elif_group);

    cur_group = elif_group;
  }

  void FileParser::begin_else_block()
  {
    if(!if_sections_stack.size())
      throw ParseError("#else Directive doesn't correspond to a previous #if/#ifdef/#ifndef directive.");

    IfSectionInfo &cur_if_section = if_sections_stack.top();
    if(cur_if_section.has_else_block)
      throw ParseError("An #if/#ifdef/#ifndef section can only have one #else block.");

    Glib::RefPtr<IfGroup> else_group = Glib::RefPtr<IfGroup>(new IfGroup);
    cur_if_section.if_section->if_groups.push_back(else_group);
    cur_if_section.has_else_block = true;

    cur_group = else_group;
  }

  void FileParser::end_if_section()
  {
    if(!if_sections_stack.size())
      throw ParseError("#endif Directive doesn't correspond to a previous #if/#ifdef/#ifndef directive.");

    if_sections_stack.pop();
    if(if_sections_stack.size())
    {
      cur_group = if_sections_stack.top().if_section->if_groups.back();
    }
    else
    {
      cur_group = output_file;
    }
  }
}

